#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <FreeRTOS.h>
#include <task.h>
#include <time.h>
#include <lwip/sockets.h>
#include "lwip/tcp.h"
#include "lwip/inet.h"
#include "lwip/netdb.h"
#include "rtc_op.h"
#include "led_op.h"
#include "wifi_execute.h"

#define NTP_SERVER          "ntp.aliyun.com"
#define NTP_PORT            123
#define TIME_ZONE           8 // E:+ / W:-
#define TIME_OFFSET_SEC     (TIME_ZONE * 3600)
#define NTP_TIMESTAMP_DELTA 2208988800ull

// Structure that defines the 48 byte NTP ntp_packet protocol.
typedef struct {
    uint8_t li_vn_mode;         // Eight bits. li, vn, and mode.
                                // li.   Two bits.   Leap indicator.
                                // vn.   Three bits. Version number of the protocol.
                                // mode. Three bits. Client will pick mode 3 for client.

    uint8_t stratum;            // Eight bits. Stratum level of the local clock.
    uint8_t poll;               // Eight bits. Maximum interval between successive messages.
    uint8_t precision;          // Eight bits. Precision of the local clock.

    uint32_t root_delay;         // 32 bits. Total round trip delay time.
    uint32_t root_dispersion;    // 32 bits. Max error aloud from primary clock source.
    uint32_t ref_id;             // 32 bits. Reference clock identifier.

    uint32_t ref_tm_s;           // 32 bits. Reference time-stamp seconds.
    uint32_t ref_tm_f;           // 32 bits. Reference time-stamp fraction of a second.

    uint32_t orig_tm_s;          // 32 bits. Originate time-stamp seconds.
    uint32_t orig_tm_f;          // 32 bits. Originate time-stamp fraction of a second.

    uint32_t rx_tm_s;            // 32 bits. Received time-stamp seconds.
    uint32_t rx_tm_f;            // 32 bits. Received time-stamp fraction of a second.

    uint32_t tx_tm_s;            // 32 bits and the most important field the client cares about. Transmit time-stamp seconds.
    uint32_t tx_tm_f;            // 32 bits. Transmit time-stamp fraction of a second.
} ntp_packet_t;

int get_net_time(void)
{
    int sd, ret = 0;
    ntp_packet_t ntp_packet;
    
    struct sockaddr_in serv_addr;
    struct hostent *server;

    struct timeval tv;

    sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // Create a UDP socket.
    if (sd < 0) {
        perror("Open socket");
        ret = sd;
        goto err_1;
    }

    server = gethostbyname(NTP_SERVER); // Convert URL to IP.
    if (server == NULL) {
        perror("NTP server not found");
        ret = -1;
        goto err_2;
    }

    bzero((char *)&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
    serv_addr.sin_port = htons(NTP_PORT);
    ret = connect(sd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    if (ret < 0) {
        perror("Connect to ntp server");
        goto err_2;
    }

    memset(&ntp_packet, 0, sizeof(ntp_packet_t));
    ntp_packet.li_vn_mode = 0x1B; // Set the first byte's bits to 00,011,011 for li = 0, vn = 3, and mode = 3. The rest will be left set to zero.
    ret = write(sd, (char *)&ntp_packet, sizeof(ntp_packet_t));
    if (ret < 0) {
        perror("Send request to ntp server");
        goto err_2;
    }

    tv.tv_sec = 1;
    tv.tv_usec = 0;
    setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval));

    ret = read(sd, (char *)&ntp_packet, sizeof(ntp_packet_t));
    if (ret < 0) {
        perror("Receive response from ntp server");
        goto err_2;
    }

    // These two fields contain the time-stamp seconds as the ntp_packet left the NTP server.
    // The number of seconds correspond to the seconds passed since 1900.
    // ntohl() converts the bit/byte order from the network's to host's "endianness".
    ntp_packet.tx_tm_s = ntohl(ntp_packet.tx_tm_s); // Time-stamp seconds.
    ntp_packet.tx_tm_f = ntohl(ntp_packet.tx_tm_f); // Time-stamp fraction of a second.

    // Extract the 32 bits that represent the time-stamp seconds (since NTP epoch) from when the ntp_packet left the server.
    // Subtract 70 years worth of seconds from the seconds since 1900.
    // This leaves the seconds since the UNIX epoch of 1970.
    // (1900)------------------(1970)**************************************(Time Packet Left the Server)
    time_t txTm = (time_t)(ntp_packet.tx_tm_s - NTP_TIMESTAMP_DELTA + TIME_OFFSET_SEC);
    set_rtc_time_stamp(txTm);
    
    char *wday[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
    struct tm *p;
    p=gmtime(&txTm); /*变量t的值转换为实际日期时间的表示格式*/
    printf("%d年%02d月%02d日",(1900+p->tm_year), (1+p->tm_mon),p->tm_mday);
    printf(" %s ", wday[p->tm_wday]);
    printf("%02d:%02d:%02d\r\n", p->tm_hour, p->tm_min, p->tm_sec);

    ret = 0;

err_2:
    close(sd);

err_1:
    return ret;
}

void ntp_task(void *args)
{
    int ret;

    while (1) {
        if (is_set_led_stat(LED_STAT_NO_NETWORK)) {
            vTaskDelay(1000);
            continue;
        }

        ret = get_net_time();
        if (ret != 0) {
            vTaskDelay(1000);
            continue;
        }
        set_wifi_connected(1);
        vTaskDelay(7200000);
    }
}

static TaskHandle_t ntp_task_handle;

void init_ntp(void)
{
    xTaskCreate(ntp_task, (char *)"ntp task", 256, NULL, 5, &ntp_task_handle);
}

void stop_ntp(void)
{
    vTaskDelete(ntp_task_handle);
}
